package org.jvm.classfile;

import org.jvm.classfile.attributeinfo.*;

import java.util.Arrays;

/**
 * author : wangsixiang02
 * date : 2023/1/7
 * desc :类成员，包含字段和方法
 */
public class MemberInfo {

    /**
     * 常量池
     */
    private final ConstantPool constantPool;

    /**
     * 访问标识
     */
    private final int accessFlags;

    /**
     * 成员名在常量池中的坐标
     */
    private final int nameIndex;

    /**
     * 成员描述在常量池中的坐标（成员类型、方法返回值、方法形参列表等）
     */
    private final int descriptorIndex;

    /**
     * 属性
     */
    private final AttributeInfo[] attributes;

    private String mockName;

    public MemberInfo(ConstantPool constantPool, int accessFlags, int nameIndex, int descriptorIndex, AttributeInfo[] attributes) {
        this.constantPool = constantPool;
        this.accessFlags = accessFlags;
        this.nameIndex = nameIndex;
        this.descriptorIndex = descriptorIndex;
        this.attributes = attributes;
    }

    public int getAccessFlags() {
        return accessFlags;
    }

    /**
     * 根据索引从常量池中返回成员名
     *
     * @return
     */
    public String name() {
        if (mockName != null) {
            return mockName;
        }
        return constantPool.getUtf8(nameIndex);
    }

    public void setMockName(String mockName) {
        this.mockName = mockName;
    }

    /**
     * 根据索引从常量池中返回成员描述
     *
     * @return
     */
    public String descriptor() {
        return constantPool.getUtf8(descriptorIndex);
    }

    public CodeAttribute getCodeAttribute() {
        for (AttributeInfo attributeInfo : this.attributes) {
            if (attributeInfo instanceof CodeAttribute) {
                return (CodeAttribute) attributeInfo;
            }
        }
        return null;
    }

    public ConstantValueAttribute getConstantValueAttribute() {
        for (AttributeInfo attributeInfo : this.attributes) {
            if (attributeInfo instanceof ConstantValueAttribute) {
                return (ConstantValueAttribute) attributeInfo;
            }
        }
        return null;
    }

    public byte[] getRuntimeVisibleAnnotationsAttributeData() {
        return getUnparsedAttributeData("RuntimeVisibleAnnotations");
    }

    public byte[] getRuntimeVisibleParameterAnnotationsAttributeData() {
        return getUnparsedAttributeData("RuntimeVisibleParameterAnnotationsAttribute");
    }

    public byte[] getAnnotationDefaultAttributeData() {
        return getUnparsedAttributeData("AnnotationDefault");
    }

    private byte[] getUnparsedAttributeData(String name) {
        return Arrays.stream(this.attributes)
                .filter(attributeInfo -> attributeInfo instanceof UnparsedAttribute)
                .map(attributeInfo -> (UnparsedAttribute) attributeInfo)
                .filter(attributeInfo -> attributeInfo.getName().equals(name))
                .map(UnparsedAttribute::getInfo)
                .findAny()
                .orElse(new byte[0]);

    }

}
